/*!
 * @file        apm32f4xx_fmc.c
 *
 * @brief       This file provides all the FMC firmware functions
 *
 * @version     V1.0.2
 *
 * @date        2022-06-23
 *
 * @attention
 *
 *  Copyright (C) 2021-2022 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be usefull and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

#include "apm32f4xx_fmc.h"
#include "apm32f4xx_rcm.h"

/** @addtogroup APM32F4xx_StdPeriphDriver
  @{
*/

/** @defgroup FMC_Driver
  * @brief FMC driver modules
  @{
*/

/** @defgroup FMC_Functions
  @{
*/

/*!
 * @brief     Configs the code latency value.
 *
 * @param     latency: the FMC Latency value.
 *            This parameter can be one of the following values:
 *            @arg FMC_LTNCY_0: FMC 0 Latency cycle
 *            @arg FMC_LTNCY_1: FMC 1 Latency cycle
 *            @arg FMC_LTNCY_2: FMC 2 Latency cycles
 *            @arg FMC_LTNCY_3: FMC 3 Latency cycles
 *            @arg FMC_LTNCY_4: FMC 4 Latency cycles
 *            @arg FMC_LTNCY_5: FMC 5 Latency cycles
 *            @arg FMC_LTNCY_6: FMC 6 Latency cycles
 *            @arg FMC_LTNCY_7: FMC 7 Latency cycles
 *
 * @retval    None
 */
void FMC_ConfigLatency(FMC_LATENCY_T latency)
{
    *(__IO uint8_t *)ACCTRL_BYTE0_ADDRESS = (uint8_t)latency;
}

/*!
 * @brief     Enable the Prefetch Buffer.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_EnablePrefetchBuffer(void)
{
    FMC->ACCTRL |= FMC_ACCTRL_PREFEN;
}

/*!
 * @brief     Disable the Prefetch Buffer.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_DisablePrefetchBuffer(void)
{
    FMC->ACCTRL &= (~FMC_ACCTRL_PREFEN);
}

/*!
 * @brief     Enable the Instruction Cache feature.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_EnableInstructionCache(void)
{
    FMC->ACCTRL |= FMC_ACCTRL_ICACHEEN;
}

/*!
 * @brief     Disable the Instruction Cache feature.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_DisableInstructionCache(void)
{
    FMC->ACCTRL &= (~FMC_ACCTRL_ICACHEEN);
}

/*!
 * @brief     Enable the Data Cache feature.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_EnableDataCache(void)
{
    FMC->ACCTRL |= FMC_ACCTRL_DCACHEEN;
}

/*!
 * @brief     Disable the Data Cache feature.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_DisableDataCache(void)
{
    FMC->ACCTRL &= (~FMC_ACCTRL_DCACHEEN);
}

/*!
 * @brief     Reset the Instruction Cache feature.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_ResetInstructionCache(void)
{
    FMC->ACCTRL |= FMC_ACCTRL_ICACHERST;
}

/*!
 * @brief     Reset the Data Cache feature.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_ResetDataCache(void)
{
    FMC->ACCTRL |= FMC_ACCTRL_DCACHERST;
}

/*!
 * @brief     Unlocks the FMC control register access.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_Unlock(void)
{
  if((FMC->CTRL & FMC_CTRL_LOCK) != RESET)
    {
        FMC->KEY = FMC_KEY1;
        FMC->KEY = FMC_KEY2;
    }
}

/*!
 * @brief     Locks the FMC control register access.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_Lock(void)
{
    FMC->CTRL |= FMC_CTRL_LOCK;
}

/*!
 * @brief     Erases a specified FMC Sector.
 *
 * @param     sector: The Sector number to be erased.
 *            This parameter can be a value between FMC_SECTOR_0 and FMC_SECTOR_11.
 *
 * @param     voltageRange: The device voltage range which defines the erase parallelism.
 *            This parameter can be one of the following values:
 *              @arg FMC_VOLTAGE_1: when the device voltage range is 1.8V to 2.1V,
 *                                  the operation will be done by byte (8-bit)
 *              @arg FMC_VOLTAGE_2: when the device voltage range is 2.1V to 2.7V,
 *                                  the operation will be done by half word (16-bit)
 *              @arg FMC_VOLTAGE_3: when the device voltage range is 2.7V to 3.6V,
 *                                  the operation will be done by word (32-bit)
 *              @arg FMC_VOLTAGE_4: when the device voltage range is 2.7V to 3.6V + External Vpp,
 *                                  the operation will be done by double word (64-bit)
 *
 * @retval    FMC_STATUS_T: The returned value can be:
 *            FMC_BUSY, FMC_ERROR_PROGRAM, FMC_ERROR_WRP, FMC_ERROR_OPERATION or FMC_COMPLETE.
 */
FMC_STATUS_T FMC_EraseSector(FMC_SECTOR_T sector, FMC_VOLTAGE_T voltageRange)
{
    uint32_t tmp_psize = 0x00;
    FMC_STATUS_T status = FMC_COMPLETE;

    if (voltageRange == FMC_VOLTAGE_1)
    {
        tmp_psize = FMC_PSIZE_BYTE;
    }
    else if (voltageRange == FMC_VOLTAGE_2)
    {
        tmp_psize = FMC_PSIZE_HALF_WORD;
    }
    else if (voltageRange == FMC_VOLTAGE_3)
    {
        tmp_psize = FMC_PSIZE_WORD;
    }
    else if (voltageRange == FMC_VOLTAGE_4)
    {
        tmp_psize = FMC_PSIZE_DOUBLE_WORD;
    }

    status = FMC_WaitForLastOperation();

    if (status == FMC_COMPLETE)
    {
        FMC->CTRL &= 0xFFFFFCFF;
        FMC->CTRL |= tmp_psize;
        FMC->CTRL &= 0xFFFFFF07;
        FMC->CTRL |= FMC_CTRL_SERS | sector;
        FMC->CTRL |= FMC_CTRL_START;
        status = FMC_WaitForLastOperation();
        FMC->CTRL &= (~FMC_CTRL_SERS);
        FMC->CTRL &= 0xFFFFFF07;
    }

    return status;
}

/*!
 * @brief     Clears all FMC Sectors.
 *
 * @param     voltageRange: The device voltage range which defines the erase parallelism.
 *            This parameter can be one of the following values:
 *              @arg FMC_VOLTAGE_1: when the device voltage range is 1.8V to 2.1V,
 *                                  the operation will be done by byte (8-bit)
 *              @arg FMC_VOLTAGE_2: when the device voltage range is 2.1V to 2.7V,
 *                                  the operation will be done by half word (16-bit)
 *              @arg FMC_VOLTAGE_3: when the device voltage range is 2.7V to 3.6V,
 *                                  the operation will be done by word (32-bit)
 *              @arg FMC_VOLTAGE_4: when the device voltage range is 2.7V to 3.6V + External Vpp,
 *                                  the operation will be done by double word (64-bit)
 *
 * @retval    FMC_STATUS_T: The returned value can be:
 *            FMC_BUSY, FMC_ERROR_PROGRAM, FMC_ERROR_WRP, FMC_ERROR_OPERATION or FMC_COMPLETE.
 */
FMC_STATUS_T FMC_EraseAllSectors(FMC_VOLTAGE_T voltageRange)
{
    uint32_t tmp_psize = 0x00;
    FMC_STATUS_T status = FMC_COMPLETE;
    status = FMC_WaitForLastOperation();

    if (voltageRange == FMC_VOLTAGE_1)
    {
        tmp_psize = FMC_PSIZE_BYTE;
    }
    else if (voltageRange == FMC_VOLTAGE_2)
    {
        tmp_psize = FMC_PSIZE_HALF_WORD;
    }
    else if (voltageRange == FMC_VOLTAGE_3)
    {
        tmp_psize = FMC_PSIZE_WORD;
    }
    else if (voltageRange == FMC_VOLTAGE_4)
    {
        tmp_psize = FMC_PSIZE_DOUBLE_WORD;
    }

    if (status == FMC_COMPLETE)
    {
        FMC->CTRL &= 0xFFFFFCFF;
        FMC->CTRL |= tmp_psize;
        FMC->CTRL |= FMC_CTRL_MERS;
        FMC->CTRL |= FMC_CTRL_START;
        status = FMC_WaitForLastOperation();
        FMC->CTRL &= (~FMC_CTRL_MERS);
    }

    return status;
}

/*!
 * @brief     Programs a double word (64-bit) at a specified address.
 *
 * @param     address:the address to be programmed.
 *            ((address) >= 0x08000000) && ((address) <= 0x080FFFFF))
 *            ((address) >= 0x1FFF7800) && ((address) <= 0x1FFF7A0F)))
 *
 * @param     data: the data to be programmed.
 *
 * @retval    FMC_STATUS_T: The returned value can be:
 *            FMC_BUSY, FMC_ERROR_PROGRAM, FMC_ERROR_WRP, FMC_ERROR_OPERATION or FMC_COMPLETE.
 */
FMC_STATUS_T FMC_ProgramDoubleWord(uint32_t address, uint64_t data)
{
    FMC_STATUS_T status = FMC_COMPLETE;
    status = FMC_WaitForLastOperation();

    if (status == FMC_COMPLETE)
    {
        FMC->CTRL &= 0xFFFFFCFF;
        FMC->CTRL |= FMC_PSIZE_DOUBLE_WORD;
        FMC->CTRL |= FMC_CTRL_PG;
        *(__IO uint64_t*)address = data;
        status = FMC_WaitForLastOperation();
        FMC->CTRL &= (~FMC_CTRL_PG);
    }

    return status;
}

/*!
 * @brief     Programs a word (32-bit) at a specified address.
 *
 * @param     address:the address to be programmed.
 *            ((address) >= 0x08000000) && ((address) <= 0x080FFFFF))
 *            ((address) >= 0x1FFF7800) && ((address) <= 0x1FFF7A0F)))
 *
 * @param     data: the data to be programmed.
 *
 * @retval    FMC_STATUS_T: The returned value can be:
 *            FMC_BUSY, FMC_ERROR_PROGRAM, FMC_ERROR_WRP, FMC_ERROR_OPERATION or FMC_COMPLETE.
 */

FMC_STATUS_T FMC_ProgramWord(uint32_t address, uint32_t data)
{
    FMC_STATUS_T status = FMC_COMPLETE;
    status = FMC_WaitForLastOperation();

    if (status == FMC_COMPLETE)
    {
        FMC->CTRL &= 0xFFFFFCFF;
        FMC->CTRL |= FMC_PSIZE_WORD;
        FMC->CTRL |= FMC_CTRL_PG;
        *(__IO uint32_t*)address = data;
        status = FMC_WaitForLastOperation();
        FMC->CTRL &= (~FMC_CTRL_PG);
    }

    return status;
}

/*!
 * @brief     Programs a half word (16-bit) at a specified address.
 *
 * @param     address:the address to be programmed.
 *            ((address) >= 0x08000000) && ((address) <= 0x080FFFFF))
 *            ((address) >= 0x1FFF7800) && ((address) <= 0x1FFF7A0F)))
 *
 * @param     data: the data to be programmed.
 *
 * @retval    FMC_STATUS_T: The returned value can be:
 *            FMC_BUSY, FMC_ERROR_PROGRAM, FMC_ERROR_WRP, FMC_ERROR_OPERATION or FMC_COMPLETE.
 */
FMC_STATUS_T FMC_ProgramHalfWord(uint32_t address, uint16_t data)
{
    FMC_STATUS_T status = FMC_COMPLETE;
    status = FMC_WaitForLastOperation();

    if (status == FMC_COMPLETE)
    {
        FMC->CTRL &= 0xFFFFFCFF;
        FMC->CTRL |= FMC_PSIZE_HALF_WORD;
        FMC->CTRL |= FMC_CTRL_PG;
        *(__IO uint16_t*)address = data;
        status = FMC_WaitForLastOperation();
        FMC->CTRL &= (~FMC_CTRL_PG);
    }

    return status;
}

/*!
 * @brief     Programs a byte (8-bit) at a specified address.
 *
 * @param     address:the address to be programmed.
 *            ((address) >= 0x08000000) && ((address) <= 0x080FFFFF))
 *            ((address) >= 0x1FFF7800) && ((address) <= 0x1FFF7A0F)))
 *
 * @param     data: the data to be programmed.
 *
 * @retval    FMC_STATUS_T: The returned value can be:
 *            FMC_BUSY, FMC_ERROR_PROGRAM, FMC_ERROR_WRP, FMC_ERROR_OPERATION or FMC_COMPLETE.
 */
FMC_STATUS_T FMC_ProgramByte(uint32_t address, uint8_t data)
{
    FMC_STATUS_T status = FMC_COMPLETE;
    status = FMC_WaitForLastOperation();

    if (status == FMC_COMPLETE)
    {
        FMC->CTRL &= 0xFFFFFCFF;
        FMC->CTRL |= FMC_PSIZE_BYTE;
        FMC->CTRL |= FMC_CTRL_PG;
        *(__IO uint8_t*)address = data;
        status = FMC_WaitForLastOperation();
        FMC->CTRL &= (~FMC_CTRL_PG);
    }

    return status;
}

/*!
 * @brief     Unlocks the option bytes block access
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_UnlockOptionByte(void)
{
    if ((FMC->OPTCTRL & FMC_OPTCTRL_OPTLOCK) != RESET)
    {
        FMC->OPTKEY = FMC_OPT_KEY1;
        FMC->OPTKEY = FMC_OPT_KEY2;
    }
}

/*!
 * @brief     Locks the option bytes block access
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_LockOptionByte(void)
{
    FMC->OPTCTRL |= FMC_OPTCTRL_OPTLOCK;
}

/*!
 * @brief     Enable the write protection of the desired sectors,
 *            for the first 1 Mb of the FMC
 *
 * @param     wrp: specifies the sector(s) to be write protected or unprotected.
 *            This parameter can be one of the following values:
 *              @arg wrp: The value between FMC_OPT_WRP_SECTOR_0 and FMC_OPT_WRP_SECTOR_11
 *              @arg FMC_OPT_WRP_SECTOR_All
 *
 * @retval    None
 */
void FMC_OPT_EnableWriteProtect(FMC_OPT_WRP_T wrp)
{
    FMC_STATUS_T status = FMC_COMPLETE;
    status = FMC_WaitForLastOperation();

    if (status == FMC_COMPLETE)
    {
        *(__IO uint16_t*)(OPTCTRL_BYTE2_ADDRESS) &= (~(uint16_t)wrp);
    }
}

/*!
 * @brief     Disable the write protection of the desired sectors,
 *            for the first 1 Mb of the FMC
 *
 * @param     wrp: Specifies the sector(s) to be write protected or unprotected.
 *            This parameter can be one of the following values:
 *              @arg wrp: The value between FMC_OPT_WRP_SECTOR_0 and FMC_OPT_WRP_SECTOR_11
 *              @arg FMC_OPT_WRP_SECTOR_All
 *
 * @retval    None
 */
void FMC_OPT_DisableWriteProtect(FMC_OPT_WRP_T wrp)
{
    FMC_STATUS_T status = FMC_COMPLETE;
    status = FMC_WaitForLastOperation();

    if (status == FMC_COMPLETE)
    {
        *(__IO uint16_t*)(OPTCTRL_BYTE2_ADDRESS) |= (uint16_t)wrp;
    }
}
/*!
 * @brief     Sets the read protection level.
 *
 * @param     rdp: Specifies the read protection level.
 *            This parameter can be one of the following values:
 *              @arg FMC_OPT_RDP_LV0: No protection
 *              @arg FMC_OPT_RDP_LV1: Read protection of the memory
 *
 * @retval    None
 */
void FMC_OPT_ConfigReadProtect(FMC_OPT_RDP_T rdp)
{
    FMC_STATUS_T status = FMC_COMPLETE;
    status = FMC_WaitForLastOperation();

    if (status == FMC_COMPLETE)
    {
        *(__IO uint8_t*)OPTCTRL_BYTE1_ADDRESS = rdp;
    }
}

/*!
 * @brief     Programs the FMC User Option Byte: WDTSEL / RSTSTOP / RSTSTDB.
 *
 * @param     iwdt: Selects the IWDT mode
 *            This parameter can be one of the following values:
 *              @arg FMC_OPT_IWDT_SOFT: Software IWDT selected
 *              @arg FMC_OPT_IWDT_HARD: Hardware IWDT selected
 * @param     stop: Reset event when entering STOP mode.
 *            This parameter  can be one of the following values:
 *              @arg FMC_OPT_STOP_NORST: No reset generated when entering in STOP
 *              @arg FMC_OPT_STOP_RST: Reset generated when entering in STOP
 * @param     stdby: Reset event when entering Standby mode.
 *            This parameter  can be one of the following values:
 *              @arg FMC_OPT_STDBY_NORST: No reset generated when entering in STANDBY
 *              @arg FMC_OPT_STDBY_RST: Reset generated when entering in STANDBY
 *
 * @retval    None
 */
void FMC_OPT_ConfigUser(FMC_OPT_IWDT_T iwdt, FMC_OPT_STOP_T stop, FMC_OPT_STDBY_T stdby)
{
    uint8_t option = 0xFF;
    FMC_STATUS_T status = FMC_COMPLETE;
    status = FMC_WaitForLastOperation();

    if (status == FMC_COMPLETE)
    {
        option = (uint8_t)((*(__IO uint8_t *)OPTCTRL_BYTE0_ADDRESS) & 0x0F);
        *(__IO uint8_t *)OPTCTRL_BYTE0_ADDRESS = ((uint8_t)iwdt | stop | stdby | option);
    }
}

/*!
 * @brief     Sets the Brownout Reset Level.
 *
 * @param     bor: specifies the Option Bytes BOR Reset Level.
 *            This parameter can be one of the following values:
 *              @arg FMC_OPT_BOR_LV3: Supply voltage ranges from 2.7 to 3.6 V
 *              @arg FMC_OPT_BOR_LV2: Supply voltage ranges from 2.4 to 2.7 V
 *              @arg FMC_OPT_BOR_LV1: Supply voltage ranges from 2.1 to 2.4 V
 *              @arg FMC_OPT_BOR_OFF: Supply voltage ranges from 1.62 to 2.1 V
 *
 * @retval    None
 */
void FMC_OPT_ConfigBrownoutReset(FMC_OPT_BOR_T bor)
{
    *(__IO uint8_t*)(OPTCTRL_BYTE0_ADDRESS) &= (~(FMC_OPTCTRL_BORLVL));
    *(__IO uint8_t*)(OPTCTRL_BYTE0_ADDRESS) |= bor;
}

/*!
 * @brief     Launch the option byte loading.
 *
 * @param     None
 *
 * @retval    FMC_STATUS_T: The returned value can be:
 *            FMC_BUSY, FMC_ERROR_PROGRAM, FMC_ERROR_WRP, FMC_ERROR_OPERATION or FMC_COMPLETE.
 */
FMC_STATUS_T FMC_OPT_Launch(void)
{
    FMC_STATUS_T status = FMC_COMPLETE;

    *(__IO uint8_t*)(OPTCTRL_BYTE0_ADDRESS) |= (uint32_t)FMC_OPTCTRL_OPTSTART;
    status = FMC_WaitForLastOperation();

    return status;
}

/*!
 * @brief     Returns the FMC User Option Bytes values.
 *
 * @param     None
 *
 * @retval    The FMC User Option Bytes values: WDTSEL(Bit0), RSTSTOP(Bit1)
 *            and RSTSTDBY(Bit2).
 */
uint8_t FMC_OPT_ReadUser(void)
{
    return (uint8_t)(FMC->OPTCTRL >> 5);
}

/*!
 * @brief     Returns the FMC Write Protection Option Bytes value.
 *
 * @param     None
 *
 * @retval    The FMC Write Protection Option Bytes value
 */
uint16_t FMC_OPT_ReadWriteProtect(void)
{
    return (*(__IO uint16_t *)(OPTCTRL_BYTE2_ADDRESS));
}

/*!
 * @brief     Returns the FMC Read Protection level.
 *
 * @param     None
 *
 * @retval    tmp: ReadOut Protection Status:
 *                 - SET, when FMC_OPT_RDP_LV1 is set
 *                 - RESET, when FMC_OPT_RDP_LV0 is set
 */
uint8_t FMC_OPT_ReadProtectLevel(void)
{
    uint8_t tmp = RESET;

    if ((*(__IO uint8_t*)((OPTCTRL_BYTE1_ADDRESS)) != (uint8_t)FMC_OPT_RDP_LV0))
    {
        tmp = SET;
    }

    return tmp;
}

/*!
 * @brief     Returns the FMC BOR level.
 *
 * @param     None
 *
 * @retval    The FMC BOR level:
 *              - FMC_OPT_BOR_LV1: Supply voltage ranges from 2.7 to 3.6 V
 *              - FMC_OPT_BOR_LV1: Supply voltage ranges from 2.4 to 2.7 V
 *              - FMC_OPT_BOR_LV1: Supply voltage ranges from 2.1 to 2.4 V
 *              - FMC_OPT_BOR_OFF   : Supply voltage ranges from 1.62 to 2.1 V
 */
uint8_t FMC_OPT_ReadBrownoutReset(void)
{
    return (uint8_t)(*(__IO uint8_t*)((OPTCTRL_BYTE0_ADDRESS)) & FMC_OPT_BOR_OFF);
}

/*!
 * @brief     Enables the specified FMC interrupts.
 *
 * @param     interrupt: Select the FMC interrupt sources
 *                       This parameter can be any combination of the following values:
 *                       @arg FMC_INT_ERR :  Error Interrupt
 *                       @arg FMC_INT_OC  :  Operation Complete Interrupt
 *
 * @retval    None
 */
void FMC_EnableInterrupt(uint32_t interrupt)
{
    if (interrupt == FMC_INT_ERR)
    {
        FMC->CTRL |= FMC_INT_ERR;
    }
    else
    {
        FMC->CTRL |= FMC_INT_OC;
    }
}

/*!
 * @brief     Disable the specified FMC interrupts.
 *
 * @param     interrupt: Select the FMC interrupt sources
 *                       This parameter can be any combination of the following values:
 *                       @arg FMC_INT_ERR :  Error Interrupt
 *                       @arg FMC_INT_OC  :  Operation Complete Interrupt
 *
 * @retval    None
 */
void FMC_DisableInterrupt(uint32_t interrupt)
{
    if (interrupt == FMC_INT_ERR)
    {
        FMC->CTRL &= ~(uint32_t)FMC_INT_ERR;
    }
    else
    {
        FMC->CTRL &= ~(uint32_t)FMC_INT_OC;
    }
}

/*!
 * @brief     Read the FMC flag
 *
 * @param     flag: specifies the FMC flag to check.
 *            This parameter can be one of the following values:
 *              @arg FMC_FLAG_ENDOP  : FMC End of Operation flag
 *              @arg FMC_FLAG_ERROP  : FMC operation Error flag
 *              @arg FMC_FLAG_ERRWRP : FMC Write protected error flag
 *              @arg FMC_FLAG_ERRPGA : FMC Programming Alignment error flag
 *              @arg FMC_FLAG_ERRPGP : FMC Programming Parallelism error flag
 *              @arg FMC_FLAG_ERRPGS : FMC Programming Sequence error flag
 *              @arg FMC_FLAG_BUSY   : FMC Busy flag
 *
 * @retval    SET or RESET.
 */
uint8_t FMC_ReadStatusFlag(FMC_FLAG_T flag)
{
    uint8_t temp = RESET;

    if ((FMC->STS & flag) != (uint32_t)RESET)
    {
        temp = SET;
    }

    return temp;
}

/*!
 * @brief     Clears the FMC flag.
 *
 * @param     flag: specifies the FMC flags to clear.
 *            This parameter can be any combination of the following values:
 *              @arg FMC_FLAG_ENDOP  : FMC End of Operation flag
 *              @arg FMC_FLAG_ERROP  : FMC operation Error flag
 *              @arg FMC_FLAG_ERRWRP : FMC Write protected error flag
 *              @arg FMC_FLAG_ERRPGA : FMC Programming Alignment error flag
 *              @arg FMC_FLAG_ERRPGP : FMC Programming Parallelism error flag
 *              @arg FMC_FLAG_ERRPGS : FMC Programming Sequence error flag
 *
 * @retval    None
 */
void FMC_ClearStatusFlag(uint32_t flag)
{
    FMC->STS = flag;
}

/*!
 * @brief     Returns the FMC Status.
 *
 * @param     None
 *
 * @retval    FMC_STATUS_T: The returned value can be:
 *            FMC_BUSY, FMC_ERROR_PROGRAM, FMC_ERROR_WRP, FMC_ERROR_OPERATION or FMC_COMPLETE.
 */
FMC_STATUS_T FMC_ReadStatus(void)
{
    FMC_STATUS_T status = FMC_COMPLETE;

    if ((FMC->STS & FMC_FLAG_BUSY) == FMC_FLAG_BUSY)
    {
        status = FMC_BUSY;
    }
    else if ((FMC->STS & FMC_FLAG_ERRWRP) != RESET)
    {
        status = FMC_ERROR_WRP;
    }
    else if ((FMC->STS & (uint32_t)0xE0) != RESET)
    {
        status = FMC_ERROR_PROGRAM;
    }
    else if ((FMC->STS & FMC_FLAG_ERROP) != RESET)
    {
        status = FMC_ERROR_OPERATION;
    }
    else
    {
        status = FMC_COMPLETE;
    }

    return status;
}

/*!
 * @brief     Waits for a FMC operation to complete.
 *
 * @param     None
 *
 * @retval    FMC_STATUS_T: The returned value can be:
 *            FMC_BUSY, FMC_ERROR_PROGRAM, FMC_ERROR_WRP, FMC_ERROR_OPERATION or FMC_COMPLETE.
 */
FMC_STATUS_T FMC_WaitForLastOperation(void)
{
    __IO FMC_STATUS_T status = FMC_COMPLETE;

    status = FMC_ReadStatus();

    while (status == FMC_BUSY)
    {
        status = FMC_ReadStatus();
    }

    return status;
}

/**@} end of group FMC_Functions */
/**@} end of group FMC_Driver */
/**@} end of group APM32F4xx_StdPeriphDriver */
